package cn.com.server.common.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSAUtils {
	public static final String KEY_ALGORITHM = "RSA";
	private static final String PUBLIC_KEY = "publicKey";
	private static final String PRIVATE_KEY = "privateKey";
	
	/**
	 * RSA复杂度,和byte[] 是 KEYSIZE/8
	 */
	private static int KEY_INIT_SIZE = 1024;
	
	/**
	 * RSA最大加密明文大小,KEY_INIT_SIZE/8-11
	 */
	private static int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA最大解密密文大小,KEY_INIT_SIZE/8
	 */
	private static int MAX_DECRYPT_BLOCK = 128;
	
	static {
		KEY_INIT_SIZE = 2048;
		MAX_ENCRYPT_BLOCK = KEY_INIT_SIZE/8 - 11;
		MAX_DECRYPT_BLOCK = KEY_INIT_SIZE/8;
	}

	/**
	 * @Title: publicKeyToString
	 * @Description: 得到字符串型的公钥 Java
	 * @param publicKey
	 * @return String
	 */
	public static String publicKeyToString(RSAPublicKey publicKey) {
		return Base64.getEncoder().encodeToString(publicKey.getEncoded());
	}

	/**
	 * @Title: privateKeyToString
	 * @Description: 得到字符串型的私钥 Java
	 * @param privateKey
	 * @return String
	 */
	public static String privateKeyToString(RSAPrivateKey privateKey) {
		return Base64.getEncoder().encodeToString(privateKey.getEncoded());
	}

	/**
	 * @Title: getPublicKey
	 * @Description: 将字符串型公钥转化为PublicKey Java
	 * @param publicKey
	 * @return PublicKey
	 */
	public static PublicKey getPublicKey(String publicKey) {
		try {
			X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
			KeyFactory factory;
			factory = KeyFactory.getInstance(KEY_ALGORITHM);
			return factory.generatePublic(x509EncodedKeySpec);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取包内公钥
	 * @return
	 */
	public static String getPublicKey() {
		Properties productPro = MyResource.getSrcProperties("public.key");;
		return productPro.getProperty("KEY_VALUE").trim();
	}
	
	/**
	 * 某路径下获取公钥
	 * @param filePath
	 * @return
	 */
	public static PublicKey getPublicKeyFile(String filePath) {
		Properties productPro = MyResource.getProperties(filePath);
		return getPublicKey(productPro.getProperty("KEY_VALUE").trim());
	}

	/**
	 * @Title: getPrivateKey
	 * @Description: 将字符串型私钥转化为 PrivateKey Java
	 * @param privateKey
	 * @return PrivateKey
	 */
	public static PrivateKey getPrivateKey(String privateKey) {
		try {
			PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.trim()));
			KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
			return factory.generatePrivate(pkcs8EncodedKeySpec);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取包内私钥
	 * @return
	 */
	public static String getPrivateKey() {
		Properties productPro = MyResource.getSrcProperties("private.key");
		return productPro.getProperty("KEY_VALUE").trim();
	}
	
	/**
	 * 某路径下获取私钥
	 * @param filePath
	 * @return
	 */
	public static PrivateKey getPrivateKeyFile(String filePath) {
		Properties productPro = MyResource.getProperties(filePath);
		return getPrivateKey(productPro.getProperty("KEY_VALUE").trim());
	}

	/**
	 * @Title: generateKeyBytes
	 * @Description: 初始化密钥对
	 * @return Map<String,Object>
	 */
	public static Map<String, Object> generateKeyBytes() {
		KeyPairGenerator keyPairGen;
		try {
			keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
			keyPairGen.initialize(KEY_INIT_SIZE);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			Map<String, Object> keyMap = new HashMap<String, Object>(2);
			keyMap.put(PUBLIC_KEY, publicKey);
			keyMap.put(PRIVATE_KEY, privateKey);
			return keyMap;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * @Title: RSAEncode
	 * @Description: 将字符串加密
	 * @param key
	 * @param plainText
	 * @return String
	 */
	public static String RSAEncode(PublicKey key, String plainText) {
		byte[] b = plainText.getBytes();
		try {
			int inputLen = b.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			// 对数据分段解密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(b, offSet, MAX_ENCRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(b, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_ENCRYPT_BLOCK;
			}
			byte[] decryptedData = out.toByteArray();
			out.close();
			return Base64.getEncoder().encodeToString(decryptedData);
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
				| BadPaddingException | IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	 * @Title: RSAEncodeFile
	 * @Description: 将文件加密
	 * @param key
	 * @param filePath
	 * @return boolean
	 */
	public static boolean RSAEncodeFile(PublicKey key, String filePath) {
		try {
			//检测需要加密文件
	    	File file = new File(filePath);
			String path = file.getPath();
			if (!file.exists()) {
				return false;
			}
			
			//生成文件校验信息
			String fileMD5 = Md5Utils.fileMD5(filePath);
			//校验信息追加
			FileUtils.appendMethodFile(filePath,fileMD5);
			
			//初始化RSA
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			
			//初始化文件
			int index = path.lastIndexOf("\\");
			String destFile = path.substring(0, index) + File.separatorChar + UUID.randomUUID().toString();
			File dest = new File(destFile);
//			InputStream in = new FileInputStream(filePath);
			RandomAccessFile in = new RandomAccessFile(filePath, "r");
			OutputStream out = new FileOutputStream(destFile);
			int offSet = 0;
			byte[] buffer = new byte[MAX_ENCRYPT_BLOCK];
			byte[] cache;
			// 对数据分段加密
			while ((offSet = in.read(buffer))> 0) {
				cache = cipher.doFinal(buffer,0,offSet);
				out.write(cache, 0, cache.length);
				out.flush();
			}
			in.close();
			out.close();
			file.delete();
			dest.renameTo(new File(filePath));
			//后base64编码
			Base64Utils.EncodeFile(filePath);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	

	/**
	 * @Title: RSADecode
	 * @Description: 将字符串解密
	 * @param key
	 * @param encodedText
	 * @return String
	 */
	public static String RSADecode(PrivateKey key, String encodedText) {
		try {
			byte[] b = Base64.getDecoder().decode(encodedText);
			int inputLen = b.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, key);
			// 对数据分段解密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(b, offSet, MAX_DECRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(b, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_DECRYPT_BLOCK;
			}
			byte[] decryptedData = out.toByteArray();
			out.close();
			return new String(decryptedData);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException | IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * @Title: RSADecodeFile
	 * @Description: 将字符串解密
	 * @param key
	 * @param filePath
	 * @return boolean
	 */
	public static boolean RSADecodeFile(PrivateKey key, String filePath) {
		try {
			//检测需要解密文件
	    	File file = new File(filePath);
	    	String path = file.getPath();
			if (!file.exists()) {
				return false;
			}
			//初始化文件
			int index = path.lastIndexOf(File.separatorChar);
			String destFile = path.substring(0, index) + File.separatorChar + UUID.randomUUID().toString();
			File dest = new File(destFile);
			if (!dest.getParentFile().exists()) {
				dest.getParentFile().mkdirs();
			}
			
			Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, key);
			
			//先base64解码
			Base64Utils.DecodeFile(filePath);
			
//			InputStream in = new FileInputStream(filePath);
			RandomAccessFile in = new RandomAccessFile(filePath, "r");
			OutputStream out = new FileOutputStream(destFile);
			byte[] buffer = new byte[MAX_DECRYPT_BLOCK];
			int offSet = 0;
			byte[] cache;
			// 对数据分段解密
			while ((offSet = in.read(buffer))> 0) {
				cache = cipher.doFinal(buffer,0,offSet);
				out.write(cache, 0, cache.length);
				out.flush();
			}
			in.close();
			out.close();
			file.delete();
			dest.renameTo(new File(filePath));
			//校验MD5
			//校验码
			String MD5 = FileUtils.subtractMethodFile(filePath,32);
			String fMD5 = Md5Utils.fileMD5(filePath);
			if(fMD5.equals(MD5)) {
				return true;
			}else {
				System.out.println("MD5校验不通过");
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
//	public static void main(String[] args) throws IOException {
////		Map<String, Object> map = generateKeyBytes();
////		String publicKey = publicKeyToString((RSAPublicKey) map.get(PUBLIC_KEY));
////		String privateKey = privateKeyToString((RSAPrivateKey) map.get(PRIVATE_KEY));
////		System.out.println("生成的公钥Java--> " + publicKey);
////		System.out.println("生成的私钥Java--> " + privateKey);
//		
//		String defaultValue0 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtr0/c+mXyQPMsrSoYTpks9YTA3GAc6hXi2sQnEf3Ba+LZZUH4YEECqp3ox2eliZt+JosZ0vnbSTM9Ow6scHwCeO1UFpvB2m16cfylOwGKgWlss+gyOnA9pfEVAO1gd/CNQzeEJFEFKKRuGVX6BBbB2XAvwucuCU4B+oNyDDMfh/9ZqanIX77EKug54CmLNXuhuW9+zLyHzCKiBzLtgUHR+z/qg3WYUm6reNXlaA7MIbGw0BYO1KyMZPLCPM73pFCwuQ+3YV6dxW5cZXE0IPulj7KwP0gqfrXam2V69xFP35PSlyplA3KqN2rcdcIiYaBaVwcPfyFmN3IdoJMwkDxjwIDAQAB";
////		RSAEncodeFile(getPublicKey(defaultValue0),"D:\\test\\docv5.0.120171023140929.tiptop");
//		String defaultValue1 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC2vT9z6ZfJA8yytKhhOmSz1hMDcYBzqFeLaxCcR/cFr4tllQfhgQQKqnejHZ6WJm34mixnS+dtJMz07DqxwfAJ47VQWm8HabXpx/KU7AYqBaWyz6DI6cD2l8RUA7WB38I1DN4QkUQUopG4ZVfoEFsHZcC/C5y4JTgH6g3IMMx+H/1mpqchfvsQq6DngKYs1e6G5b37MvIfMIqIHMu2BQdH7P+qDdZhSbqt41eVoDswhsbDQFg7UrIxk8sI8zvekULC5D7dhXp3FblxlcTQg+6WPsrA/SCp+tdqbZXr3EU/fk9KXKmUDcqo3atx1wiJhoFpXBw9/IWY3ch2gkzCQPGPAgMBAAECggEACSMrYhNjGJUNDj6EUOBHmspwhvKM9ruLLRh8ayQaEPm3IawzCa6W0iW1970Kaf2f8BddcwjTmJyHdxO815iX9RaiwVLUTG2XwAnUcmfe4wkHQQqqwoEVuf7avUy2LblPj7GGBRQOcG20laDTpXHz60nGOodNHdQWmia067as8v12lFiHpkZxUG2iNhTD9KTeTxM/5qBGxWfU8UvXpdLmvLKmo0ccbiQjUq2/Fwa0uO82RaxlHAN2biAH1vWTy1f+fovqaGuhME1RmfCVQx/tPYZGCEN/22k8sPGXliLKbe2aYbZJnavT5FcYtgxUWtc14h7YXK5T5IdVfFFWWdZ4aQKBgQD/GHr21Z9yXnozflwmbDPix0KO1Cgf1KZM/14KDnI2TPGUXoQCWawWiuWw3fqcQyM31qf3uZv65p7G35e5u94l4LBzmH0LdHA6aimE6wDP+klmbVIGeqN0FjpUK/3OnM2wu0L/YlQ5IKCH1HFrAP0r5/34BvGkjvcxClRb24MCBQKBgQC3YxksrdQpFkToUO1X4REO9LcvUYacab2xxF4gSMVQ+P+wg+H4fgBtT4ORQIeQbM7hHZpE0foFXfuAw5zCC0e1zP/s/cjL0pAZS9d+LmIz0ppCnXNQpEeth2zKPyzuGnIZVuEohJNm3YwJWunXaDqIQfSQNtUWZ/CW+yc/ogKVgwKBgQCoPaYDm3pwfJ0Nzx3JfX1u6Mod1xCrhZ00lQuZiFONOovttbRMtIWJzIuWbaB2ZNUaY6kd+b1E6cr4EMlJhv0JcDoP0xdNcbr+27uMXU2I3WKBekIp4cJhsrM/hk/7b3E9SRPzyQ8RKlKVoug2kU1oIrZeWX4PcPbY0h43AvxRXQKBgQCiyKaKEGspZYkZJuznevMncXZB3H4+zWYDLCnZQn9UFxLwXsDxUPeUEGbbQoqTWuJw1VQhDrp+lSQw8nJtlJG9ARGUDtp0gp5Cth289fNZyPIBe73msUym7nVVOarqWDM75vDKhJRLVRF/JGI6QVzjopgyg1m3H+BiROJcC7V7fQKBgQCWIBUQ2SrEYuq29rZP1cc9/iyMz663Nupc8GBWLZhqjiVpeFml45eqoEEcY96Wnol0qGpI18CsKDPoJpOHH+7r8V178qztumXUABCM9jFTo5OM9SAViJ/XVfVYcKdbzPI+K5f61n8/I4JnGZOxMvvS9j5HJ14hwx32sd0FXSEzPw==";
////		RSADecodeFile(getPrivateKey(defaultValue1),"D:\\test\\docv5.0.120171023140929.tiptop");
////		//获取尾部MD5
////		String MD5 = FileUtils.subtractMethodFile("D:\\test\\docv5.0.120171023140929.tiptop", 32);
////		System.out.println(MD5);
////		System.out.println(Md5Utils.fileMD5("D:\\test\\docv5.0.120171023140929.tiptop"));
//		
//		RSAEncodeFile(getPublicKey(defaultValue0),"D:\\test\\docv5.0.120171023140929.tiptop");
//		System.out.println("0");
//		//产品号
//		String productNumber = FileUtils.Random("PRODUCT_NUMBER", 32);
//		//添加到文件内容开始位置
//		FileUtils.appendFirstFile("D:\\test\\docv5.0.120171023140929.tiptop",productNumber);
//		System.out.println("1");
//		FileUtils.subtractFirstFile("D:\\test\\docv5.0.120171023140929.tiptop", 32);
//		System.out.println("2");
//		RSADecodeFile(getPrivateKey(defaultValue1),"D:\\test\\docv5.0.120171023140929.tiptop");
//		
//		
//	}
}